液氮实验¶

准备工作¶

导入实验所需头文件

In [ ]:
import json
import plotly.graph_objects as go
import numpy as np
from scipy.optimize import curve_fit
from scipy.integrate import quad

处理三组数据,存为json格式方便调用及储存

In [2]:
def save(file_path,save_path):
    # 初始化两个空列表来存储时间和电压
    time = []
    voltage = []
    # 打开并读取文件
    with open(file_path, 'r') as file:
        for line in file:
            # 移除行末的换行符并分割数据
            parts = line.strip().split('\t')  # 按制表符分割
            if len(parts) == 2:  
                # 将时间和电压添加到对应的列表
                time.append(float(parts[0]))    # 时间
                voltage.append(float(parts[1]))  # 电压
    # 创建字典并存储时间和电压数据
    dic = {}
    dic["time"] = time
    dic["voltage"] = voltage

    # 将数据写入 JSON 文件
    with open(save_path, "w", encoding="utf-8") as file2:
        json.dump(dic, file2, indent=4, ensure_ascii=False)  # 保证中文字符能正确写入

    print(f"数据已成功保存到 {save_path}")#"物块质量.txt"
    #"液氮数据.txt"
save(r"定标.txt","dingbiao.json")
save(r"物块质量.txt","wukuai.json")
save(r"液氮数据.txt","yedan.json")
数据已成功保存到 dingbiao.json
数据已成功保存到 wukuai.json
数据已成功保存到 yedan.json

导入储存的在.json数据

In [3]:
with open("dingbiao.json","r",encoding= "UTF-8") as file:
        dingbiaodata  = json.load(file)
with open("wukuai.json","r",encoding= "UTF-8") as file:
        wukuaidata  = json.load(file)
with open("yedan.json","r",encoding= "UTF-8") as file:
        yedandata  = json.load(file)

绘制三幅散点图方便查找数据

In [4]:
def pic(data,title,begin):
    # 从 JSON 中提取时间和电压数据
    time = data["time"][begin:]
    voltage = data["voltage"][begin:]

    # 创建一个散点图
    fig = go.Figure()

    # 添加散点数据
    fig.add_trace(go.Scatter(
        x=time,
        y=voltage,
        mode='markers',  # 'markers' 表示散点
        marker=dict(size=6, color='blue'),
        text=[f'时间: {t:.4f}秒<br>电压: {v:.8f}伏特' for t, v in zip(time, voltage)],  # 提供悬停时显示的信息
        hoverinfo='text'  # 显示文本信息
    ))

    # 设置图表的标题和标签
    fig.update_layout(
        title= title,
        xaxis_title='时间 (s)',
        yaxis_title='电压 (V)',
        template='plotly',
    )

    # 显示图表
    fig.show()
pic(dingbiaodata,"砝码定标散点数据",0)
pic(wukuaidata,"物块质量散点数据",0)
pic(yedandata,"液氮散点数据",0)

发现最后一个图放入泡沫桶前后的数据变化太大,由图选取30s后的数据作为初始记录点,重新绘制下图

In [5]:
pic(yedandata,"液氮散点数据",270) #177,223|248,331|402,471|540,641

处理定标数据¶

现在对定标数据进行处理,求出不同标准砝码的电压可信区间平均值,下面是在图中得出可信区间起始点

其中选取的砝码质量为 $25n , n = 0,1,2,3...8$

In [6]:
dingbiao_begin_time = [2,40,75,146,110,185,234,272,320] 

考虑到采样频率为9hz,下标数 dingbiao_begin_index[n] =9 * dingbiao_begin_time[n]

In [7]:
dingbiao_begin_index = []
for n in range(0,len(dingbiao_begin_time)):
    dingbiao_begin_index.append(9 * dingbiao_begin_time[n])
print(dingbiao_begin_index)
[18, 360, 675, 1314, 990, 1665, 2106, 2448, 2880]

定义求一段时间内电压平均值函数

In [8]:
def averV(beginlist,data):
    avedata = []
    sumV = 0
    for a in beginlist:
        sumV = 0
        for v in data["voltage"][a:a+23 * 9]:
            sumV += v
        avrV = float(sumV) / (23 * 9)
        avedata.append(avrV)
    return avedata
dingbiao_ave = averV(dingbiao_begin_index,dingbiaodata)
for i in dingbiao_ave:
    print(i,"\t")
0.00014051120772946857 	
0.00016345531400966168 	
0.0001863969082125605 	
0.00020942574879227047 	
0.00023230082125603866 	
0.0002553376811594205 	
0.00027830739130434775 	
0.0003012528502415458 	
0.0003241877777777775 	

有了这些数据我们就可以对电压和质量进行定标做出V-M图

In [9]:
# 示例数据
X = []
for n in range(0, 9):
    X.append(25 * n)
x = np.array(X)
y = np.array(dingbiao_ave)  

# 已知的截距
intercept = 0.00014051120772946857

# 定义线性函数(固定截距,只拟合斜率)
def linear_function(x, slope):
    return slope * x + intercept  # 截距是固定的,斜率是拟合参数

# 使用 curve_fit 函数拟合数据,固定截距
params, covariance = curve_fit(linear_function, x, y, p0=[1])  # p0 = [1] 为斜率的初始值
slope_fitted = params[0]  # 拟合出的斜率

# 计算拟合直线的 y 值
y_fitted = linear_function(x, slope_fitted)

# 扩展 x 的范围,从 0 延伸到比最大值大的地方
x_extended = np.linspace(0, max(x) * 1.5, 100)  # 从 0 到 x 最大值的 1.5 倍,生成 100 个点
y_extended = linear_function(x_extended, slope_fitted)  # 计算扩展的 x 对应的 y 值

# 创建散点图和拟合直线的 Plotly 图形
fig = go.Figure()

# 添加散点数据
fig.add_trace(go.Scatter(
    x=x, 
    y=y, 
    mode='markers', 
    name='数据点', 
    marker=dict(size=8, color='blue')
))

# 添加拟合直线(包括延长部分)
fig.add_trace(go.Scatter(
    x=x_extended, 
    y=y_extended, 
    mode='lines', 
    name='拟合直线', 
    line=dict(color='red', width=2)
))

# 在图中添加斜率和截距的文本注释
slope_text = f"斜率: {slope_fitted:.8f}"
intercept_text = f"截距: {intercept:.8f}"

# 设置图表标题和坐标轴标签
fig.update_layout(
    title='散点图与线性拟合直线(固定截距)',
    xaxis_title='质量(g)',
    yaxis_title='电压 (V)',
    template='plotly',
    showlegend=True,
    annotations=[
        # 在图表上添加斜率的注释
        go.layout.Annotation(
            x=0.5,  # x 坐标位置
            y=0.9,  # y 坐标位置
            xref="paper", 
            yref="paper", 
            text=slope_text,  # 斜率文本
            showarrow=False,  # 不显示箭头
            font=dict(size=12, color="black"),  # 字体大小和颜色
            align="center"
        ),
        # 在图表上添加截距的注释
        go.layout.Annotation(
            x=0.5,
            y=0.85,
            xref="paper",
            yref="paper",
            text=intercept_text,  # 截距文本
            showarrow=False,
            font=dict(size=12, color="black"),
            align="center"
        ),
    ]
)

# 显示图表
fig.show()

# 打印拟合的斜率
print(f"拟合的斜率: {slope_fitted:.8f}")
print(f"已知的截距: {intercept:.8f}")
拟合的斜率: 0.00000092
已知的截距: 0.00014051

由此得到的质量和电压的关系为 $U = 0.00000092 m + 0.00014051$

根据定标直线求投入四个物块的质量¶

(小铜块,大铜块,不锈钢块,铝块)

求出各个物块电压平均值,先求出开始下标,取之后23s内的所有点

In [10]:
wukuai_begin_time = [8,43,83,102]
wukuai_begin_index = [72,387,747,918]

求出电压平均值(用到之前定义的aveV函数)

In [11]:
ave_wukuai = averV(wukuai_begin_index,wukuaidata)
print(ave_wukuai)
[0.00015928115942028998, 0.00019470700483091777, 0.00015566652173913025, 0.0001506016908212561]

用定标直线求出对应质量(单位g)顺序:小铜块,大铜块,钢块,铝块

In [12]:
def m(index):
    m = (index - 0.00014051) / 0.00000092 
    return m
wukuai_m = [m(ave_wukuai[0]),m(ave_wukuai[1]),m(ave_wukuai[2]),m(ave_wukuai[3])]
print(wukuai_m)
[20.403434152489115, 58.90978785969324, 16.474480151228537, 10.969229153539237]

对液氮数据进行处理,求出前后液氮质量的变化¶

先标出变化的起始结束时间点

In [13]:
yedanbegin = [177,248,402,540]#177,223|248,331|402,471|540,641
yedanend = [223,331,471,641]
def liner(begin,end,data):
    x = data["time"][begin * 9:end * 9]
    y = data["voltage"][begin * 9:end * 9]
    slope, intercept = np.polyfit(x, y, 1)
    return slope, intercept

求出五条U - T直线的斜率和截距,并保存 由前文,U = 0.00000092 m + 0.00014051

In [14]:
def U(m):
    return 0.00000092 * m + 0.00014051
def M(u):
    return (u - 0.00014051)/ 0.00000092
slopes = []
intercepts = []
for begintime in yedanbegin:
    slopei,intercepti = liner(begintime - 23,begintime,yedandata)
    slopes.append(slopei)
    intercepts.append(intercepti)
slopei,intercepti = liner(641,641 + 23,yedandata)
slopes.append(slopei)
intercepts.append(intercepti)
print(slopes,intercepts)
[np.float64(-1.8857526051511306e-08), np.float64(-1.872820462795376e-08), np.float64(-1.772900087015145e-08), np.float64(-1.9201379398350535e-08), np.float64(-1.5404261163151113e-08)] [np.float64(0.0003516805009405124), np.float64(0.0003527357644797613), np.float64(0.00036075847008853806), np.float64(0.0003730404371479554), np.float64(0.0004054118255828527)]

求出四次投入的中间时刻,并对其液氮蒸发量进行计算

In [15]:
Midtime = []
yedanM= []
for i in range(0,4):
    Midtime.append((yedanend[i] + yedanbegin[i])/2)
print("Midtime",Midtime)
shunxu =[3,2,0,1]
for n in range(0,4):
    U1 = intercepts[n+1] + slopes[n+1] * Midtime[n]
    U2 = intercepts[n] + slopes[n] * Midtime[n]
    changeU = intercepts[n+1] - intercepts[n] + Midtime[n] * (slopes[n + 1] - slopes[n]) 
    yedanM.append(M(U2) - M(U1) + wukuai_m[shunxu[n]]) #wukuai_m 顺序为小铜,大铜,钢,铝 
    print(M(U1),M(U2),M(changeU))
print(yedanM)
Midtime [200.0, 289.5, 436.5, 590.5]
226.6088299501854 225.433691011098 -151.55312193047783
233.82165688764044 224.78690134779202 -143.6935053297168
243.6402554788863 230.98887087904015 -140.07687626971907
278.0495753978391 240.42611153611895 -115.10479700784508
[np.float64(9.794090214451847), np.float64(7.439724611380118), np.float64(7.752049552642962), np.float64(21.286323997973113)]

由此获得了所有的数据,绘制出最终图片

In [16]:
def pic2(data, title, begin, slopes, intercepts):
    # 从 JSON 中提取时间和电压数据
    time = data["time"][begin:]
    voltage = data["voltage"][begin:]

    # 创建一个散点图
    fig = go.Figure()
    line_styles = ['solid', 'dash', 'dot', 'longdash', 'dashdot']
    # 添加散点数据
    fig.add_trace(go.Scatter(
        x=time,
        y=voltage,
        mode='markers',  # 'markers' 表示散点
        marker=dict(size=6, color='blue'),
        text=[f'时间: {t:.4f}秒<br>电压: {v:.8f}伏特' for t, v in zip(time, voltage)],  # 提供悬停时显示的信息
        hoverinfo='text'  # 显示文本信息
    ))
    i = 0
    # 添加五条直线
    for slope, intercept in zip(slopes, intercepts):
        # 计算直线的x轴范围
        x_range = np.linspace(min(time), max(time), 100)
        # 计算y轴上的对应点
        y_range = slope * x_range + intercept
        # 添加直线到图中
        fig.add_trace(go.Scatter(
            x=x_range,
            y=y_range,
            mode='lines',  # 'lines' 表示线条
            line=dict(color='red', width=2, dash=line_styles[i]), 
            name=f'直线: y={slope:.10f}x + {intercept:.8f}'  # 直线方程
        ))
        i += 1

    # 设置图表的标题和标签
    fig.update_layout(
        title=title,
        xaxis_title='时间 (s)',
        yaxis_title='电压 (V)',
        template='plotly',
    )

    # 显示图表
    fig.show()
pic2(yedandata,"液氮拟合直线和散点图",270,slopes,intercepts)

Cu的定压比热容Cp与温度T的关系拟合图

In [ ]:
# 样条插值
spline = CubicSpline(x_data, y_data)

# 创建拟合曲线数据
x_fit = np.linspace(min(x_data), max(x_data), 500)
y_fit = spline(x_fit)

# 绘制拟合曲线
plt.plot(x_data, y_data, 'bo', label="Data")
plt.plot(x_fit, y_fit, 'r-', label="Cubic Spline")
plt.xlabel("X")
plt.ylabel("Y")
plt.legend()
plt.show()

# 对样条插值曲线从 x=78 到 x=293 进行积分
integral, error = quad(spline, 78, 293)

print(f"从 78 到 293 的积分值: {integral}")
No description has been provided for this image
从 78 到 293 的积分值: 71111.57134382188
In [11]:
# 定义被积函数
def integrand(x):
    return x**3 / (np.exp(x) - 1)

# 积分范围 [0, 343/293]
lower_limit = 0
upper_limit = 343 / 78

# 使用quad进行数值积分
integral, error = quad(integrand, lower_limit, upper_limit)

# 输出积分结果
print(f"积分结果: {integral}")
积分结果: 4.325307030065073
In [12]:
print(9 * 6.02 *1.38 * 78**4 *4.325307030065073/(343**3))
296.64093328541276
In [15]:
print(58.91/63.546*(4.910-0.2966))
4.276829289018979
In [16]:
print(4.276829289018979/21.28 * 1000)
200.97881997269636